<?php
/**
 * @author css3 <css3@qq.com>
 */

namespace zonday\weixin;


use Yii;
use yii\base\ActionEvent;
use yii\base\ActionFilter;
use yii\base\Event;
use yii\helpers\Url;
use yii\web\Controller;

class WeixinAuthorize extends ActionFilter
{
    const SCOPE_SNSAPI_BASE = 'snsapi_base';

    const SCOPE_SNSAPI_USERINFO = 'snsapi_userinfo';

    const EVENT_WEIXIN_LOGIN = 'weixinLogin';

    /**
     * @var bool 是否自动登录
     */
    public $autoLogin = false;

    public $scopeMap = [];

    /**
     * @var string
     */
    public $scope = self::SCOPE_SNSAPI_BASE;

    /**
     * @return bool
     */
    public function beforeAction($action)
    {
        $session = Yii::$app->getSession();
        $request = Yii::$app->getRequest();

        $code = $request->get('code', '');
        $state = $request->get('state', '');

        if (isset($this->scopeMap[$action->id])) {
            $scope = $this->scopeMap[$action->id];
        } else {
            $scope = $this->scope;
        }

        $accessToken = $this->authorizeAccessToken($scope, $code, $state);

        if ($accessToken) {
            if ($this->autoLogin && !$session->get('__weixin_logged', false)) {
                $this->login($accessToken);
            }
        }
        return true;
    }

    /**
     * @param string $scope
     * @param string $code
     * @param string $state
     * @return bool|mixed
     */
    protected function authorizeAccessToken($scope = self::SCOPE_SNSAPI_BASE, $code = '', $state = '')
    {
        /** @var Controller $controller */
        $controller = $this->owner;
        /** @var Weixin $weixin */
        $weixin = Yii::$app->weixin;
        $request = Yii::$app->getRequest();

        $accessToken = $this->readAccessToken();

        if ($code !== '' && $accessToken === false) {
            $fetch = true;
        } elseif ($accessToken === false) {
            $redirect = true;
        }

        $params = $request->get();
        if (!Yii::$app->urlManager->enablePrettyUrl) {
            unset($params['r'], $params['code'], $params['state']);
        }

        if (!empty($fetch)) {
            $accessToken = $weixin->fetchAuthorizeAccessToken($code);
            $this->writeAccessToken($accessToken);
            $controller->redirect(Url::to([$controller->route, $params], true));
            Yii::$app->end();
        }

        if (!empty($redirect)) {
            Yii::trace('微信授权跳转 ' . $scope, __METHOD__);
            $controller->redirect(
                $weixin->webAuthorizeUrl(Url::to([$controller->route, $params], true), $scope, $state)
            );
            Yii::$app->end();
        }

        return $accessToken;
    }

    /**
     * 登录
     * @param $accessToken
     */
    protected function login($accessToken)
    {
        if (!$accessToken) {
            return;
        }

        $event = new Event([
            'sender' => $this,
            'data' => ['accessToken' => $accessToken]
        ]);

        $user = Yii::$app->getUser();
        $user->trigger(self::EVENT_WEIXIN_LOGIN, $event);

        Yii::trace('微信登录', __METHOD__);

        if (!$user->getIsGuest()) {
            Yii::$app->getSession()->set('__weixin_logged', true);
        }
    }

    protected function writeAccessToken($accessToken)
    {
        $session = Yii::$app->getSession();
        $session->set('__weixin_web_accessToken', $accessToken);
    }

    public function readAccessToken()
    {
        $session = Yii::$app->getSession();
        $accessToken = $session->get('__weixin_web_accessToken', false);
        if ($accessToken && $accessToken['expires'] < time()) {
            if ($accessToken['refresh_expires'] < time()) {
                $accessToken = Yii::$app->weixin->refreshAuthorizeAccessToken($accessToken['refresh_token']);
                $this->writeAccessToken($accessToken);
            }
        }
        return $accessToken;
    }
}